package dc.connectionmanager.logic;

import java.util.ArrayList;

import dc.connectionmanager.persistance.IConnection;

public abstract class AConnectionManager implements IConnectionManager 
{
	protected ArrayList<IConnection> connectionList;
	
	private int maxConnection;
	private int minConnection;
	private int idleTimeLimit;
	private int threadSleepLimit;
	private int updaterIntervall;
	private int aktStartingConnections;
	
	protected String ipAdress;
	protected String username;
	protected String password;
	protected ICommand cmd;
	protected PlattformUpdater pu = null;
	
	
	protected AConnectionManager()
	{
		this.connectionList = new ArrayList<IConnection>();
		
		this.maxConnection = 6;
		this.minConnection = 1;
		this.idleTimeLimit = 45;
		this.threadSleepLimit = 150;
		this.updaterIntervall = 2000;
		this.aktStartingConnections = 0;
		
		this.startPlattformUpdater();
	}
	
	@Override
	public int getMaxConnection() 
	{
		return this.maxConnection;
	}

	@Override
	public int getMinConnection() 
	{
		return this.minConnection;
	}

	@Override
	public int getIdleTimeLimit() 
	{
		return this.idleTimeLimit;
	}
	
	@Override
	public int getUpdaterInterval()
	{
	    return this.updaterIntervall;
	}

	@Override
	public void setMaxConnection(int nMaxConnection) 
	{
		if(nMaxConnection > 0 && nMaxConnection >= this.getMinConnection())
		{
			this.maxConnection = nMaxConnection;
		}
	}

	@Override
	public void setMinConnection(int nMinConnection) 
	{
		if(nMinConnection > 0 && nMinConnection <= this.getMaxConnection())
		{
			this.minConnection = nMinConnection;
		}

	}

	@Override
	public void setIdleTimeLimit(int nIdleTimeLimit) 
	{
		if(nIdleTimeLimit > 0)
		{
			this.idleTimeLimit = nIdleTimeLimit;
		}
	}
	
	@Override
	public void setUpdaterInterval(int time)
	{
	    if(time > 0)
	    {
	        this.updaterIntervall = time;
	    }
	}

	@Override
	public abstract void connect();

	@Override
	public final IConnection getAvailableConnection() 
	{
		short count = 0;
		short m_count = 0;
		
		while(true)
		{
			if(connectionList.size() < getMinConnection())
			{
				if(checkConnPoolLimit())
				{
					startConnection();
				}
			}
			
			if(count > 9)
			{
				count = 0;
				if(checkConnPoolLimit())
				{
					startConnection();
				}
			}
			
			if(m_count > 50)
			{
				if(!isConnected())
				{
				    try
                    {
                        Thread.sleep(this.threadSleepLimit);
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
				}
			}
			
			try
			{
				int connCount = 0;
				
				while (connectionList.size() > connCount)
				{
					if(connectionList.get(connCount).isConnected())
					{
						if(!connectionList.get(connCount).isBusy())
						{
							return connectionList.get(connCount);
						}
						else
						{
							++count;
							Thread.sleep(this.threadSleepLimit);
						}
					}
					else
					{
						connectionList.get(connCount).closeConnection();
						connectionList.remove(connCount);
					}
					++connCount;
				}
			}
			catch(Exception e)
			{
				System.out.println(e.getMessage());
			}
			finally
			{
				++m_count;
			}
		}
	}

	@Override
	public final boolean createNewConnection(IConnection conn)
	{
		try
		{
			if(conn.connect())
			{
				connectionList.add(conn);
				return true;
			}
			
			return false;
		}
		catch(Exception e)
		{
			System.out.println(e.getMessage());
			return false;
		}
		finally
		{
			--aktStartingConnections;
		}
	}

	@Override
	public Object sendCommand(String command) 
	{
		return getAvailableConnection().sendCommand(command);
	}

	@Override
	public void sendCommand(IArgument arg) 
	{
		new AsyncCommandHandler(this).runCommand(arg);
	}
	
	@Override
	public void sendCommandNC(String command)
	{
		getAvailableConnection().sendCommandNC(command);
	}

	@Override
	public final boolean isConnected() 
	{
		try
		{
			int connCount = 0;
			while(connectionList.size() > connCount)
			{
				if(connectionList.get(connCount).isConnected())
				{
					return true;
				}
				else
				{
					connectionList.get(connCount).closeConnection();
					connectionList.remove(connCount);
				}
				++connCount;
			}
			return false;
		}
		catch(Exception e)
		{
			System.out.println(e.getMessage());
			return false;
		}
		finally
		{}
	}

	@Override
	public final void checkConnectionIdle() 
	{
		try
		{
			int connCount = 0;
			while(connectionList.size() > connCount)
			{
				if(connectionList.get(connCount).getIdleTime() > getIdleTimeLimit())
				{
					if(connectionList.size() > getMinConnection())
					{
						connectionList.get(connCount).closeConnection();
						connectionList.remove(connCount);
					}
				}
				++connCount;
			}
		}
		catch(Exception e)
		{
			System.out.println(e.getMessage());
		}
		finally
		{}
	}

	@Override
	public boolean isReachable() 
	{
		// TODO ping to host
		return true;
	}
	
	@Override
	public final boolean checkConnPoolLimit()
	{
		return ((connectionList.size() + aktStartingConnections) < getMaxConnection());
	}
	
	@Override
	public final void startConnection()
	{
		++aktStartingConnections;
		new AsyncStartConnection(this);
	}
	
	@Override
	public int getCountConnections()
	{
	    return this.connectionList.size();
	}
	
	@Override
	public void registerCommandClass(ICommand obj)
	{
	    this.cmd = obj;
	}
	
	@Override
	public ICommand getCommandClass()
	{
	    return this.cmd;
	}
	
	@Override
	public void startPlattformUpdater()
	{
	    if(this.pu == null)
	    {
	        this.pu = new PlattformUpdater(this);
	    }
	}
	
	@Override
	public void registerConnStatusFunc(IUpdaterConnStatus obj)
	{
	    if(this.pu != null)
	    {
	        this.pu.registerTriggerConnStatusObj(obj);
	    }
	}
	
	@Override
	public void registerConnTriggerFunc(IUpdaterTriggerFunc obj)
	{
	    if(this.pu != null)
	    {
	        this.pu.registerTriggerFunctionObj(obj);
	    }
	}

}
